package com.greensam.pixelengine.websocket.handle;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;
import com.greensam.pixelengine.event.pixel.PixelEvent;
import com.greensam.pixelengine.event.pixel.PixelEventBus;
import com.greensam.pixelengine.event.pixel.PixelEventType;
import com.greensam.pixelengine.event.wrapped.PictureEditEventParams;
import com.greensam.pixelengine.pojo.vo.user.response.LoginUserVo;
import com.greensam.pixelengine.websocket.constant.MessageCode;
import com.greensam.pixelengine.websocket.constant.PictureEditActionEnum;
import com.greensam.pixelengine.websocket.constant.WebSocketEventType;
import com.greensam.pixelengine.websocket.dto.WebSocketPushMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.HashMap;
import java.util.Map;

/**
 * 图片编辑 Websocket 入口处理器
 *
 * @author Ma Chengrui
 * @since 2025/10/31 14:30
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class PictureEditHandler extends TextWebSocketHandler {

    private final PixelEventBus pixelEventBus;

    private final PictureEditBroadcaster broadcaster;

    /**
     * WebSocket 连接建立成功
     *
     * @param session 会话
     * @author Ma Chengrui
     * @since 2025/10/31
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        // 保存会话到集合中
        LoginUserVo loginUser = (LoginUserVo) session.getAttributes().get("user");
        Long pictureId = (Long) session.getAttributes().get("pictureId");

        // 注册会话到广播器
        broadcaster.addSession(pictureId, session);

        // 发布连接事件
        PictureEditEventParams param = PictureEditEventParams.builder()
                .code(MessageCode.CONNECT)
                .pictureId(pictureId)
                .user(loginUser)
                .build();
        pixelEventBus.publish(new PixelEvent(PixelEventType.PICTURE_USER_CONNECT, param));
    }

    /**
     * 接收客户端消息，根据消息类别处理消息
     *
     * @param session 会话
     * @param message 消息
     * @author Ma Chengrui
     * @since 2025/10/31
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 从 Session 中获取公共参数
        LoginUserVo user = (LoginUserVo) session.getAttributes().get("user");
        Long pictureId = (Long) session.getAttributes().get("pictureId");
        // 解析消息
        WebSocketPushMessage<Map<String, Object>> pictureEditMessage = JSONUtil.toBean(message.getPayload(), new TypeReference<>() {
        }, true);
        // 获取消息类型
        MessageCode messageCode = pictureEditMessage.getCode();
        Map<String, Object> data = pictureEditMessage.getData();

        // 发布事件
        switch (messageCode) {
            case ENTER_EDIT -> {
                // 用户进入编辑状态，设置当前正在编辑的用户
                broadcaster.setEditingUser(pictureId, user.getId());
                PictureEditEventParams param = buildEventParams(session, messageCode, PictureEditActionEnum.fromCode(data.get("action").toString()), pictureId, user);
                pixelEventBus.publish(new PixelEvent(PixelEventType.PICTURE_ENTER_EDIT, param));
            }
            case EDIT_ACTION -> {
                PictureEditEventParams param = buildEventParams(session, messageCode, PictureEditActionEnum.fromCode(data.get("action").toString()), pictureId, user);
                pixelEventBus.publish(new PixelEvent(PixelEventType.PICTURE_EDIT_ACTION, param));
            }
            case EXIT_EDIT -> {
                // 用户退出编辑状态，移除正在编辑的用户
                Long editingUserId = broadcaster.getEditingUser(pictureId);
                if (editingUserId != null && editingUserId.equals(user.getId())) {
                    broadcaster.setEditingUser(pictureId, null);
                }
                PictureEditEventParams param = buildEventParams(session, messageCode, PictureEditActionEnum.fromCode(data.get("action").toString()), pictureId, user);
                pixelEventBus.publish(new PixelEvent(PixelEventType.PICTURE_EXIT_EDIT, param));
            }
            default -> {
                Map<String, Object> param = new HashMap<>();
                param.put("message", "无效的操作事件");
                WebSocketPushMessage<Map<String, Object>> pushMessage = new WebSocketPushMessage<>(WebSocketEventType.ERROR, MessageCode.ERROR, param);
                session.sendMessage(new TextMessage(JSONUtil.toJsonStr(pushMessage)));
            }
        }
    }

    /**
     * 构建事件参数
     */
    public PictureEditEventParams buildEventParams(WebSocketSession session, MessageCode messageCode, PictureEditActionEnum action, Long pictureId, LoginUserVo user) {
        return PictureEditEventParams.builder()
                .code(messageCode)
                .action(action)
                .pictureId(pictureId)
                .user(user)
                .build();
    }

    /**
     * Websocket 连接关闭，移除用户编辑状态
     *
     * @param session 会话
     * @param status  状态
     * @author Ma Chengrui
     * @since 2025/10/31
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        Map<String, Object> attributes = session.getAttributes();
        LoginUserVo loginUser = (LoginUserVo) attributes.get("user");
        Long pictureId = (Long) attributes.get("pictureId");

        // 从广播器中移除会话
        broadcaster.removeSession(pictureId, session);
        // 检查是否该用户正在编辑图片，如果是，则移除正在编辑的用户
        if (broadcaster.getEditingUser(pictureId) != null && broadcaster.getEditingUser(pictureId).equals(loginUser.getId())) {
            broadcaster.setEditingUser(pictureId, null);
        }

        // 发布断开连接事件
        PictureEditEventParams param = PictureEditEventParams.builder()
                .code(MessageCode.DISCONNECT)
                .pictureId(pictureId)
                .user(loginUser)
                .build();
        pixelEventBus.publish(new PixelEvent(PixelEventType.PICTURE_USER_DISCONNECT, param));
    }

}
